/**
 *  JSimProd - Java simulation code for production processes
 *  Copyleft Jorge Mira Yagüe <jorge.mira.yague@gmail.com>
 *  Universidad de Alicante 2009
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package lib.model;

import lib.xml.XMLizable;
import lib.exceptions.InvalidLengthException;
import lib.exceptions.OverlappedIntervalException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import lib.model.queue.*;
import lib.plan.Item;

/**
 * This class represents a station, a station can perform a collection of tasks
 * with any length. It also has calendars with the shifts, the programmed
 * repairs and the jobs performed. The station has also a queue with the
 * outstanding tasks.
 * @see StationCalendar
 * @see Queue
 * @see Task
 * @author Jorge Mira Yagüe
 */
public class Station implements XMLizable {

    protected String name;
    protected List<String> tasks;
    protected List<String> connexions;
    protected StationCalendar repairs;
    protected StationCalendar shifts;
    protected StationCalendar jobs;
    protected Queue queue;
    protected QueueType queueType;
    protected boolean booked;
    protected int queueLength;

    /**
     * Builds an empty station
     */
    public Station() {
        this.name = "";
        tasks = new ArrayList<String>();
        connexions = new ArrayList<String>();
        repairs = new StationCalendar();
        shifts = new StationCalendar();
        jobs = new StationCalendar();
        queueLength = 0;
        booked = false;
    }

    /**
     * Builds a station with the given parameters
     * @param name the name of the station
     * @param queueType the type of the queue that the station has
     */
    public Station(String name, QueueType queueType) {
        this.name = name;
        this.queueType = queueType;
        tasks = new ArrayList<String>();
        connexions = new ArrayList<String>();
        repairs = new StationCalendar();
        shifts = new StationCalendar();
        jobs = new StationCalendar();
        switch (queueType) {
            case FIFO:
                queue = new FIFOQueue();
                break;
            case LIFO:
                queue = new LIFOQueue();
                break;
            case PFIFO:
                queue = new PFIFOQueue();
                break;
            case PLIFO:
                queue = new PLIFOQueue();
                break;
            case RSS:
                queue = new RSSQueue();
                break;
        }
        queueLength = 0;
    }

    /**
     * Adds a connection with another station
     * @param station the name of the station to make a connection with
     */
    public void addConnexion(String station) {
        connexions.add(station);
    }

    /**
     * Removes a connection with another station
     * @param station the name of the station whose connection is going to be
     * removed
     */
    public void removeConnexion(String station) {
        connexions.remove(station);
    }

    public String getConnection(int index) {
        return connexions.get(index);
    }

    public List<String> getConnections() {
        return connexions;
    }

    public QueueType getQueueType(){
        return queueType;
    }

    /**
     * Adds a task to the station
     * @param task the task is going to be added
     * @return true (as specified by Collection.add)
     */
    public boolean addTask(String task) {
        return tasks.add(task);
    }

    /**
     * Removes the specified task
     * @param task the task to be removed
     * @return true if the task existed previously on the station
     */
    public boolean removeTask(String task) {
        return tasks.remove(task);
    }

    public String getTask(int index) {
        return tasks.get(index);
    }

    public List<String> getTasks() {
        return tasks;
    }

    /**
     * Adds an interval indicating when the station is not operative
     * @param interval the repair interval to be added
     */
    public void addRepair(Interval interval) throws OverlappedIntervalException {
        repairs.addInterval(interval);
    }

    /**
     * Adds a collection of intervals indicating when the station is not
     * operative
     * @param intervals the repair intervals to be added
     */
    public void addRepairs(List<Interval> intervals)
            throws OverlappedIntervalException {
        repairs.addIntervals(intervals);
    }

    /**
     * Removes an interval indicating when the station is not operative
     * @param interval the repair interval to be removed
     */
    public void removeRepair(Interval interval) {
        repairs.removeInterval(interval);
    }

    /**
     * Adds a shift to the station
     * @param interval the shift to be added
     */
    public void addShift(Interval interval) throws OverlappedIntervalException {
        shifts.addInterval(interval);
    }

    /**
     * Adds a collection of shifts to the station
     * @param intervals the shifts to be added
     */
    public void addShifts(List<Interval> intervals)
            throws OverlappedIntervalException {
        shifts.addIntervals(intervals);
    }

    /**
     * Removes shift from the station
     * @param interval the shift to be removed
     */
    public void removeShift(Interval interval) {
        shifts.removeInterval(interval);
    }

    public StationCalendar getShifts() {
        return shifts;
    }

    public StationCalendar getRepairs() {
        return repairs;
    }

    public String getName() {
        return name;
    }

    /**
     * Adds a job to the sation, in a date when doesn't overlap any repair
     * @param job the job to be added
     * @param sameShift indicates if the job must be done in the same shift
     * @return the end date of the given job
     */
    public Calendar addJob(Interval job, boolean sameShift)
            throws InvalidLengthException, OverlappedIntervalException {

        if (!sameShift) {
            return repairs.nextNonOverlappingSlot(job);
        }

        Calendar calShift, calRepair;
        Interval tmp = new Interval(job);

        boolean fits = false;
        do {
            calRepair = repairs.nextNonOverlappingSlot(tmp);
            calShift = shifts.nextContainerSlot(tmp);
            if (calShift.equals(calRepair)) {
                fits = true;
                tmp = new Interval(calRepair, job.Length());
            } else {
                tmp = new Interval(calRepair.after(calShift) ? calRepair : calShift,
                        job.Length());
            }
        } while (!fits);
        jobs.addInterval(tmp);

        return tmp.getEnd();
    }

    /**
     * Flushes all previous jobs in the station
     */
    public void FlushJobs() {
        jobs = new StationCalendar();
    }

    /**
     * Adds an item to the machine's queue
     * @param item the item to be added
     */
    public void QueueItem(Item item) {
        queue.add(item);
    }

    public boolean isQueueEmpty() {
        return queue.isEmpty();
    }

    /**
     * Returns the item in the head of the queue and removes it form the
     * queue
     * @return the in the head of the queue, or null if the queue is empty
     */
    public Item pollItem() {
        return queue.poll();
    }

    /**
     * Returns the item in the head of the queue but doesn't remove it form the
     * queue
     * @return the in the head of the queue, or null if the queue is empty
     */
    public Item peekItem() {
        return queue.peek();
    }

    public int getQueueLength() {
        return queueLength;
    }

    public void increaseQueueLength() {
        queueLength++;
    }

    public void decreaseQueueLength() {
        queueLength--;
    }

    public void setBooked(boolean booked) {
        this.booked = booked;
    }

    public boolean isBooked() {
        return booked;
    }

    public String toXML() {
        String result = "<Station ";
        result += "name=\"" + name + "\" ";
        result += "queueType=\"" + queueType + "\" ";
        result += ">\n";
        result += "<repairs>\n" + repairs.toXML() + "</repairs>\n";
        result += "<shifts>\n" + shifts.toXML() + "</shifts>\n";
        if (tasks != null) {
            for (String task : tasks) {
                result += "<taskName name=\"" + task + "\"/>\n";
            }
        }
        if (connexions != null) {
            for (String connexion : connexions) {
                result += "<connexionName name=\"" + connexion + "\"/>\n";
            }
        }
        result += "</Station>\n";
        return result;
    }
}
